package com.code.soulqlexpress.util;

import com.code.soulqlexpress.config.QlExpressRunner;
import com.google.common.base.Splitter;
import com.ql.util.express.DefaultContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;


/**
 * 计算告警数据
 * <p>表达式：ge(大于等于) ,gt(大于) ,eq（等于）,le(小于等于)，lt(小于),in(存在)</p>
 * eg:
 * <li>ge=2  --> value >= 2 </li>
 * <li>gt=2  --> value > 2 </li>
 * <li>eq=2  --> value == 2 </li>
 * <li>le=2  --> value <= 2 </li>
 * <li>lt=2  --> value < 2 </li>
 * <li>in=2,3  --> value == 2 || value == 3 </li>
 * <li>ge=1;le=3 --> value >= 1 && value <=3 </li>
 *
 * @author jdy
 * @date 5:10 下午
 **/
@Slf4j
public class AlarmCalculationUtils {

    private final static Splitter SEMICOLON = Splitter.on(";").omitEmptyStrings().trimResults();

    private enum RELATIONAL {
        /**
         * 大于等于
         */
        GE("ge", ">=", "<"),
        /**
         * 大于
         */
        GT("gt", ">", "<="),
        /**
         * 等于
         */
        EQ("eq", "==", "!="),

        /**
         * 小于等于
         */
        LE("le", "<=", ">"),
        /**
         * 小于
         */
        LT("lt", "<", ">="),

        /**
         * 存在
         */
        IN("in", "in", "in");

        private String relationalName;
        private String relationalStr;
        private String contraryRelationalStr;

        RELATIONAL(String relationalName, String relationalStr, String contraryRelationalStr) {
            this.relationalName = relationalName;
            this.relationalStr = relationalStr;
            this.contraryRelationalStr = contraryRelationalStr;
        }

        public static RELATIONAL cast(String relationalName) {
            for (RELATIONAL cur : RELATIONAL.values()) {
                if (StringUtils.equals(relationalName, cur.relationalName)) {
                    return cur;
                }
            }
            return null;
        }

        public String getRelationalName() {
            return relationalName;
        }

        public String getRelationalStr() {
            return relationalStr;
        }

        public String getContraryRelationalStr() {
            return contraryRelationalStr;
        }
    }

    /**
     * 通过符号获取
     *
     * @param symbol 操作符
     * @return
     */
    public static String getRelationalStr(String symbol) {
        for (RELATIONAL relational : RELATIONAL.values()) {
            if (relational.getRelationalName().equals(symbol)) {
                return relational.getRelationalStr();
            }
        }
        return null;
    }

    /**
     * 表达式是否告警处理
     *
     * @param formula 表达式
     * @param value   实时值
     * @return
     */
    public static Boolean alarm(String formula, double value) {
        if (StringUtils.isBlank(formula)) {
            return Boolean.FALSE;
        }
        return alarm(SEMICOLON.splitToList(formula), value);
    }

    private static Boolean alarm(List<String> formulaList, double value) {
        return alarm(formulaList, value, new DefaultContext<>());
    }

    private static Boolean alarm(List<String> formulaList, double value, Map<String, Double> dynamicValue) {
        if (CollectionUtils.isEmpty(formulaList)) {
            return Boolean.FALSE;
        }
        final List<String> formulaListRelational = formulaList.stream()
                .filter(StringUtils::isNotBlank)
                .map(item -> formula(item))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (CollectionUtils.isEmpty(formulaListRelational)) {
            return Boolean.FALSE;
        }
        return alarm1(formulaListRelational, value);
    }

    private static Boolean alarm1(final List<String> formulaList, double value) {
        DefaultContext<String, Object> context = new DefaultContext<String, Object>();
        context.put("value", value);
        Boolean res;
        for (String cur : formulaList) {
            res = exec(context, cur);
            if (res != null && cur.contains("in")) {
                return !res;
            } else if (res != null && res) {
                return res;
            }
        }
        return Boolean.FALSE;
    }

    private static Boolean exec(DefaultContext<String, Object> context, String cur) {
        try {
            return (Boolean) QlExpressRunner.expressRunner.execute(cur, context, null, true, false);
        } catch (Exception e) {
            log.error("[PANIC]this formula is wrong {}", cur, e);
        }
        return null;
    }

    private static String formula(String formula) {
        final String[] relationalInfo = StringUtils.split(formula, "=");
        if (relationalInfo.length != 2) {
            log.warn("[PANIC]this formula is wrong {}", formula);
            return null;
        }
        final RELATIONAL relational = RELATIONAL.cast(relationalInfo[0].trim());
        if (relational == null) {
            log.warn("[PANIC]this formula relational is wrong {}", formula);
            return null;
        }
        final StringBuilder sb = new StringBuilder();
        String targetValue = relationalInfo[1];
        switch (relational) {
            case IN:
                getRelationStrForIn(targetValue, sb);
                break;
            default:
                getDefaultRelationStr(relational, targetValue, sb);
        }
        return sb.toString();
    }

    private static void getRelationStrForIn(String targetValue, StringBuilder sb) {
        sb.append("value ").append("in").append("(").append(targetValue).append(")");
    }

    private static void getDefaultRelationStr(RELATIONAL relational, String targetValue, StringBuilder sb) {
        sb.append("value ").append(relational.getContraryRelationalStr()).append(targetValue);
    }
}
